
DEBUG = False

import os, sys, shutil, math
import subprocess
from amplitude.dict import Dict
import win32com.client as w32c

class ArtFile(Dict):

    def __init__(self, fn, **args):
        Dict.__init__(self, **args)
        self.fn = fn

    def app(self):
        return w32c.Dispatch("Photoshop.Application")
        
    def img(self):
        if os.path.exists(self.fn):
            photoshop = w32c.Dispatch("Photoshop.Application")
            if os.path.splitext(self.fn)[1] == '.pdf':
                # set PDF open options
                pdfopts = w32c.Dispatch("Photoshop.PDFOpenOptions")
                pdfopts.CropPage = 4            # crop to trim box
                pdfopts.AntiAlias = True        # better text appearance
                pdfopts.Resolution = 1200       # open the file at high resolution, whatever the output
                doc = photoshop.Open(self.fn, pdfopts)
            else:
                doc = photoshop.Open(self.fn)
                if doc.Mode == 5:       # bitmap -- can't save it that way.
                    doc.ChangeMode(1)       # grayscale instead
                elif doc.Mode == 3:     # CMYK
                    doc.ChangeMode(2)       # use RGB instead
            return doc

    def save_img(self):
        """"""

    def process_magick(self, outfn=None, overwrite=True, format=None, quality=8, jpg_matte=4, jpg_format=1, 
                width=None, height=None, maxwh=2048, maxw=None, maxh=None, maxpixels=3.2e6, scale=1, res=300, 
                resample_method=4, orient=None, alpha=None):
        """process the image and output a new file"""
        # use ImageMagick for most operations. The one thing it can't do well is process a PDF -- use Photoshop for that if it's available.
        outfn = outfn or self.fn
        if DEBUG==True:
            print "Art.process_magick():"
            print "  self.fn:  ", self.fn
            print "  outfn:    ", outfn
            print "  isfile:   ", os.path.isfile(outfn)
            print "  overwrite:", overwrite
            
        if overwrite==False and os.path.isfile(outfn): 
            return
            
        use_ps = False
        try:
            im = w32c.Dispatch("ImageMagickObject.MagickImage")
        except:
            if DEBUG==True: print >> sys.stderr, "ImageMagickObject not available for " + self.fn
            use_ps = True

        if use_ps==True or os.path.splitext(self.fn)[1].lower()=='.pdf':
            if DEBUG==True: print "use_ps"
            self.make_jpg(outfn=outfn, overwrite=overwrite, jpg_quality=quality, jpg_matte=jpg_matte, jpg_format=jpg_format,
                          width=width, height=height, maxw=maxw, maxh=maxh, maxwh=maxwh, maxpixels=maxpixels, scale=scale, 
                          res=res, resample_method=resample_method, orient=orient)
            return

        if outfn != self.fn:
            shutil.copy(self.fn, outfn)
        
        w,h=[int(n) for n in im.Convert(self.fn, "-format", "%w,%h", "info:").split(',')]
        
        args = []

        if alpha is not None:
            args += ['-alpha', alpha]
        if format is not None:
            args += ['-format', format]
        geom_arg = None
        if scale != 1:
            geom_arg = str(scale*100)+'%'
        elif width is not None and height is not None:
            geom_arg = "%dx%d!" % (width, height)
        elif width is not None:
            geom_arg = "%d" % width
        elif height is not None:
            geom_arg = 'x%d' % height
        elif maxwh is not None:
            geom_arg = "%dx%d>" % (maxwh, maxwh)
        elif maxw is not None and maxh is not None:
            geom_arg = "%dx%d>" % (maxw, maxh)
        elif maxw is not None:
            geom_arg = "%d>" % maxw
        elif maxh is not None:
            geom_arg = "x%d>" % maxh
        if geom_arg is not None:
            args += ['-resize', geom_arg]

        if orient is not None:
            if orient == 'vertical' and w > h and w > maxw:
                args += ['-rotate', '-90']
            elif orient == 'horizontal' and h > w and h > maxh:
                args += ['-rotate', '90']
        
        args += ['-quality', "%d0" % quality]
        
        args += [outfn]
        
        if DEBUG==True: print '  ', args
        
        im.Mogrify(*args)

        # check to make sure maxpixels is met, and downsize if not
        w,h=[int(n) for n in im.Convert(outfn, "-format", "%w,%h", "info:").split(',')]
        if w*h > maxpixels:
            downratio = math.sqrt(float(maxpixels) / float(w*h))
            geom_arg = "%dx%d>" % (int(w*downratio), int(h*downratio))
            im.Mogrify('-resize', geom_arg, '-quality', "%d0" % quality, outfn)
            if DEBUG==True: print '\n', outfn, '\n  ', w, h, w*h, maxpixels, downratio, geom_arg

    def make_large_jpg(self, **args):
        outfn = os.path.splitext(self.fn)[0] + '_large.jpg'
        self.make_jpg(outfn=outfn, maxwh=800, **args)
        
    def make_small_jpg(self, **args):
        outfn = os.path.splitext(self.fn)[0] + '_small.jpg'
        self.make_jpg(outfn=outfn, maxwh=300, **args)
        
    def make_thumb_jpg(self, **args):
        outfn = os.path.splitext(self.fn)[0] + '_thumb.jpg'
        self.make_jpg(outfn=outfn, maxwh=50, **args)

    def make_jpg(self, outfn=None, overwrite=False, evals=[], 
                pdf_crop_page=2, pdf_resolution=1200, pdf_anti_alias=True,
                jpg_quality=9, jpg_matte=4, jpg_format=1, colorspace='rgb',
                width=None, height=None, maxw=None, maxh=None, maxwh=2048, maxpixels=3.2e6, scale=1, res=300, resample_method=4, orient=None):
        """open the source artfile from the archive and save to jpg for use in web apps.
            doc: An open photoshop document representing the artfile
            outfn: the full path to the output file
            overwrite: whether or not to overwrite an existing jpg
            jpg_quality: jpg quality, 1-12
            colorspace: default 'rgb'; could also be 'cmyk' or 'grey'
            width: resize to this width
            height: resize to this height. Note that specifying both width & height can distort the image
            maxw, maxh, maxwh: the maximum width or height to use. No distortion results
            maxpixels: the maximum number of megapixels (Apple devices are currently limited to 3.2MP, so use that)
            scale: amount to scale the image, relative to 1
            res: resample to this resolution (dpi)
            resample_method: the method to use in resampling the image
                1 = no resampling
                2 = nearest neighbor
                3 = bilinear
                4 = bicubic
                5 = bicubicsharper
                6 = bicubicsmoother
            orient: None means no change, 'vertical' or 'horizontal' means make it so.
        ---------
        Examples:
        ---------
        art = Artfile('Some/Archive/Path')
        art.make_jpg()                            # creates a jpg with the same resolution as the source.
        art.make_jpg(height=600, jpg_quality=7)   # good for screen resolution
        art.make_jpg(maxwh=100, jpg_quality=7)    # good for small thumbnails
        art.make_jpg(res=1200)                    # pre-press
        art.make_jpg(outfn='Some/Other/Path')
        """
        color_spaces = {'grey': 1, 'gray': 1, 'rgb': 2, 'cmyk': 3, 'lab': 4, 'bitmap': 5, 'indexed': 6, 'multichannel': 7}
        
        if outfn is None:
            outfn = os.path.splitext(self.fn)[0] + '.jpg'
        
        if overwrite==False and os.path.isfile(outfn):
            return
        
        photoshop = w32c.Dispatch("Photoshop.Application")

        if not os.path.exists(outfn) or overwrite==True:
            # open the art file
            ext = os.path.splitext(self.fn)[1].lower()
            if ext == '.pdf':
                # set PDF open options
                pdfopts = w32c.Dispatch("Photoshop.PDFOpenOptions")
                pdfopts.CropPage = pdf_crop_page        # default 2 - crop to crop box
                pdfopts.AntiAlias = pdf_anti_alias      # default True - better text appearance
                pdfopts.Resolution = pdf_resolution     # default 1200 - open at high resolution, whatever the output
                doc = photoshop.Open(self.fn, pdfopts)
            elif ext == '.eps':
                epsopts = w32c.Dispatch("Photoshop.EPSOpenOptions")
                epsopts.AntiAlias = True        # better text appearance
                epsopts.Resolution = 1200       # open the file at high resolution, whatever the output
                doc = photoshop.Open(self.fn, epsopts)
            else:
                doc = photoshop.Open(self.fn)

            # save as jpg
            opt = w32c.Dispatch("Photoshop.JPEGSaveOptions")
            opt.Quality = jpg_quality
            opt.Matte = jpg_matte
            opt.FormatOptions = jpg_format
            photoshop.Preferences.RulerUnits = 1            # measurements in pixels, please.
            
            # set color mode
            #doc.ChangeMode(color_spaces[colorspace])
            
            # evals
            for ev in evals:
                eval(ev)
            
            # orientation?
            if orient == 'vertical' and doc.Width > doc.Height and doc.Width > maxw:
                doc.RotateCanvas(-90)
            elif orient == 'horizontal' and doc.Width < doc.Height and doc.Height > maxh:
                doc.RotateCanvas(90)
                
            if width is not None or height is not None:
                doc.ResizeImage(Width=width, Height=height, Resolution=res, ResampleMethod=resample_method)

            if scale != 1:
                width = doc.Width * float(scale)
                height = doc.Height * float(scale)
                doc.ResizeImage(Width=width, Height=height, Resolution=res, ResampleMethod=resample_method)

            if maxw is not None and doc.Width > maxw:
                doc.ResizeImage(Width=maxw, Resolution=res, ResampleMethod=resample_method)
                
            if maxh is not None and doc.Height > maxh:
                width = doc.Width * (maxh / float(doc.Height))
                doc.ResizeImage(Width=width, Height=maxh, Resolution=res, ResampleMethod=resample_method)

            if maxwh is not None and (doc.Width > maxwh or doc.Height > maxwh):
                # resize the larger of width and height, if it's currently larger than maxwh
                if doc.Width > doc.Height:
                    if doc.Width > maxwh:
                        width = maxwh
                        height = None
                else:
                    if doc.Height > maxwh:
                        height = maxwh
                        width = None
                doc.ResizeImage(Width=width, Height=height, Resolution=res, ResampleMethod=resample_method)

            # make sure the image is less than the maximum number of pixels allowed in this context.
            wxh = doc.Height * doc.Width
            if wxh > maxpixels:
                downratio = math.sqrt(float(maxpixels) / float(wxh))
                doc.ResizeImage(Width=doc.Width*downratio, Height=doc.Height*downratio, Resolution=res, ResampleMethod=resample_method)

            if doc.Mode == 5:       # bitmap -- can't save it that way.
                doc.ChangeMode(1)       # grayscale instead
            elif doc.Mode == 3:     # CMYK
                doc.ChangeMode(2)       # use RGB instead

            doc.SaveAs(outfn, opt, AsCopy=True)
            doc.Close(Saving=2) # Don't save changes to source file.

    def make_png(self, interlaced=True):
        outfn = os.path.splitext(self.fn)[0] + '.png'
        photoshop = w32c.Dispatch("Photoshop.Application")
        photoshop.Preferences.RulerUnits = 1            # measurements in pixels, please.
        if os.path.splitext(self.fn)[1] == '.pdf':
            pdfopts = w32c.Dispatch("Photoshop.PDFOpenOptions")
            pdfopts.CropPage = 4            # crop to trim box
            pdfopts.AntiAlias = True        # better text appearance
            doc = photoshop.Open(self.fn, pdfopts)
        else:
            doc = photoshop.Open(self.fn)

        # save as png
        opt = w32c.Dispatch("Photoshop.PNGSaveOptions")
        opt.Interlaced = interlaced
        doc.SaveAs(outfn, opt, AsCopy=True)
        doc.Close(Saving=2) # Don't save changes to source file.            
        
    ILLUSTRATOR_EXPORT_FORMATS = {
        '.jpg': 1,
        '.psd': 2,
        '.svg': 3,
        '.png': 5, 
        '.gif': 6,
        '.dwg': 7,
    }
        
    def info(self, format="%w,%h"):
        """use ImageMagick to get the info about the art in the given format, default '%w,%h'"""
        im = w32c.Dispatch("ImageMagickObject.MagickImage")
        if os.path.isfile(self.fn):
            return im.Convert(self.fn, '-format', format, 'info:')

    def make_item_xml(self, title, typename, product):
        from amplitude.files.xml import XML
        from lxml import etree
        from amplitude import string as s
        name = s.hyphenify(title)
        doc = XML("")
        doc.fn = os.path.splitext(self.fn)[0]+'.xml'
        item = doc.root = etree.fromstring(
            "<item name='%s' typename='%s' product='%s'><title>%s %s</title><body/></item>" 
            % (name, typename, product, title, typename))
        item.text = '\n\t'
        item.find('title').tail = '\n\t'
        body = item.find('body'); body.text = body.tail = '\n'
        p = etree.fromstring("<p class='artfile'><img src='%s' alt='%s %s'/></p>" % (os.path.basename(self.fn), title, item.find('title').text))
        p.tail='\n\t'
        body.append(p)
        return doc
            
    @classmethod
    def montage(cls, outfn, imgfns, width=None, rowheight=None, colspace=None, options=None):
        """use Illustrator to create an image montage.
            if width is None, just put everything on one row
            if rowheight is None, use the height of the first image
            if colspace is None, use 5% of rowheight
        """
        illustrator = w32c.Dispatch("Illustrator.Application")
        if rowheight is None:
            im = w32c.Dispatch("ImageMagickObject.MagickImage")
            h = int(im.Convert(imgfns[0], '-format', "%h", 'info:'))
        else: 
            h = rowheight
        if colspace is None: colspace = int(h * .05)
        w = 0
        doc = illustrator.Documents.Add()
        for imgfn in imgfns:
            item = doc.PlacedItems.Add()
            item.File = imgfn
            scale = 100*h/item.Height
            item.Resize(scale, scale)
            item.Translate(-item.Position[0] + w, h-item.Position[1])
            w += item.Width + colspace
        w -= colspace   # remove final colspace
        doc.Artboards[0].ArtboardRect = (0, h, w, 0)
        doc.Export(outfn, Art.ILLUSTRATOR_EXPORT_FORMATS[os.path.splitext(outfn)[1].lower()], options)
        pdfopts = w32c.Dispatch("Illustrator.PDFSaveOptions")
        pdffn = os.path.splitext(outfn)[0]+'.pdf'
        doc.SaveAs(pdffn, pdfopts)
        doc.Close()
        
            
Art = ArtFile